#include <func.h>
#include "BlockQ.h"

BlockQ* blockq_create(void)
{
    BlockQ* Q = (BlockQ*)malloc(sizeof(BlockQ));
    if(Q == NULL)
        error(1, errno, "malloc");

    Q->front = 0;
    Q->rear = 0;
    Q->size = 0;

    pthread_mutex_init(&Q->mutex, NULL);
    pthread_cond_init(&Q->not_empty, NULL);
    pthread_cond_init(&Q->not_full, NULL);

    return Q;
}

void blockq_destroy(BlockQ* Q)
{
    pthread_cond_destroy(&Q->not_full);
    pthread_cond_destroy(&Q->not_empty);
    pthread_mutex_destroy(&Q->mutex);

    free(Q);
}

bool blockq_empty(BlockQ* Q)
{
    pthread_mutex_lock(&Q->mutex);
    int sz = Q->size;
    pthread_mutex_unlock(&Q->mutex);
    return sz == 0;
}

bool blockq_full(BlockQ* Q)
{
    pthread_mutex_lock(&Q->mutex);
    int sz = Q->size;
    pthread_mutex_unlock(&Q->mutex);
    return sz == N;
}

void blockq_push(BlockQ* Q, E val)
{
    pthread_mutex_lock(&Q->mutex);
    while(Q->size == N)
        pthread_cond_wait(&Q->not_full, &Q->mutex);

    Q->elements[Q->rear] = val;
    Q->rear = (Q->rear + 1) % N;
    Q->size++;

    pthread_cond_signal(&Q->not_empty);

    pthread_mutex_unlock(&Q->mutex);
}

E blockq_pop(BlockQ* Q)
{
    pthread_mutex_lock(&Q->mutex);
    while(Q->size == 0)
        pthread_cond_wait(&Q->not_empty, &Q->mutex);

    E ret = Q->elements[Q->front];
    Q->front = (Q->front + 1) % N;
    Q->size--;

    pthread_cond_signal(&Q->not_full);

    pthread_mutex_unlock(&Q->mutex);
    return ret;
}

E blockq_peek(BlockQ* Q)
{
    pthread_mutex_lock(&Q->mutex);
    while(Q->size == 0)
        pthread_cond_wait(&Q->not_empty, &Q->mutex);
    E ret = Q->elements[Q->front];

    pthread_mutex_unlock(&Q->mutex);

    return ret;
}
